#可变与不可变数据类型
"""
    python中的变量名不是简单的使用指针，而是引用机制，变量名引用在内存中存储数据所在的地址，从而操作数据。
    确保了数据的一致性
"""
list1 = [0, 'str', [1, 2]]
#int不可变类型
# list[0]:  0x7ff8b3bf4388
# list[0]:  0x7ff8b3bf43a8
print('list[0]: ', hex(id(list1[0])))
list1[0] = 1
print('list[0]: ', hex(id(list1[0])))
#string是不可变类型
# list[1]:  0x1ffc4633210
# list[1]:  0x7ff8b3c021c8
print('list[1]: ', hex(id(list1[1])))
list1[1] = 'string'
print('list[1]: ', hex(id(list1[1])))
#列表是可变类型
# list[2]:  0x1ffc46660c0
# list[0]:  0x1ffc46660c0
print('list[2]: ', hex(id(list1[2])))
list1[2][0] = 3
print('list[2]: ', hex(id(list1[2])))
print('list1: ', hex(id(list1)))
list1.append('hello')
print('list1: ', hex(id(list1)))

key1 = {
    'name' : 'marry',
    'age' : 18,
    'friend' : ['mick', 'jack'],
    'hobby' : {
        'food' : '吃鸡'
    }
}
#字典是可变类型数据
print('key[name]=marry :', hex(id(key1['name'])))
key1['name'] = 'jhon'
print('key[name]=jhon :', hex(id(key1['name'])))

print('key[hobby] :', hex(id(key1['hobby'])))
key1['hobby']['food'] = '苹果'
print('key[hobby] :', hex(id(key1['hobby'])))

print('key:', hex(id(key1)))
key1['sex'] = 'female'
print('key :', hex(id(key1)))

#集合是可变类型
set1 = {'1', 'str'}
print('set1: ', hex(id(set1)))
set1.add(3.14)
print('set1: ', hex(id(set1)))


#浅拷贝与直接赋值
    #直接赋值
list2 = list1
print(list1)
print(list2)
list1.append(3.14)
print(list2)
# list1:  0x1abea0aa2c0
# list2:  0x1abea0aa2c0
print('list1: ', hex(id(list1)))
print('list2: ', hex(id(list2)))

    #浅拷贝
    #list3:  0x1e93fd7ca40, 由此可见浅拷贝为新变量开辟了一新内存单元
list3 = list1.copy()
print(list3)
print('list3: ', hex(id(list3)))
#浅拷贝后，两个列表引用元素的内存单元地址是否相同(不可变类型)？ 由此可见只是对于容器本身进行了‘拷贝’
# list1[1]的内存地址:  0x7ff8af1221c8
# list3[1]的内存地址:  0x7ff8af1221c8
print('list1[1]的内存地址: ', hex(id(list1[1])))
print('list3[1]的内存地址: ', hex(id(list3[1])))
#那么看一下对于可变类型的情况，答案是相同的
print('list1[2]的内存地址: ', hex(id(list1[2])))
print('list3[2]的内存地址: ', hex(id(list3[2])))
#修应浅拷贝后的列表中的不可变类型数据：是否会影响到原列表呢？ 答案是不会
# list1的元素:  [1, 'string', [3, 2], 'hello', 3.14]
# list3的元素:  [1, 'change', [3, 2], 'hello', 3.14]
list3[1] = 'change'
print('list1的元素: ', list1)
print('list3的元素: ', list3)

#进一步想，list1和list3只是容器本身的地址不同，对于其内元素的引用是相同的
#那么，对于不可变类型数据，修改后两者对应引用元素的地址是否还会相同？ 答案是，修改后内存地址也随之变化
# list1[1]中元素的地址： 0x7ff8af1221c8
# list3[1]中元素的地址： 0x1f178939e30
print('list1[1]中元素的地址：', hex(id(list1[1])))
print('list3[1]中元素的地址：', hex(id(list3[1])))

#对于可变类型数据呢？
# list1[2]中元素的地址： 0x1bb9bca60c0
# list3[2]中元素的地址： 0x1bb9bca60c0
#答案是不会变化，对于列表中的可变类型数据如：列表，想象为一个一个容器，最外层引用下一层容器的地址
#我们改变的不过是下一层容器中引用元素的地址
list3[2][0] = 4
print('list1的元素: ', list1)
print('list3的元素: ', list3)
print('list1[2]中元素的地址：', hex(id(list1[2])))
print('list3[2]中元素的地址：', hex(id(list3[2])))


""""
    浅拷贝与直接赋值对比:
直接赋值：直接赋值不会创建新对象，只是让新变量名指向原对象的内存地址。修改原对象时，新变量引用的对象也会改变。示例中 list2 = list1 后，两者的内存地址相同，修改 list1 会影响 list2。
浅拷贝：使用 copy() 方法进行浅拷贝会创建一个新的容器对象，新容器和原容器的内存地址不同，但它们内部的元素仍然引用相同的内存地址。示例中 list3 = list1.copy() 后，list3 和 list1 的内存地址不同，但内部元素的地址部分相同。
浅拷贝对不同类型元素的影响
不可变类型元素：修改浅拷贝后容器中的不可变类型元素（如 int、string），不会影响原容器中的对应元素。因为修改不可变类型时，会创建一个新的对象，改变引用地址。例如修改 list3[1] 后，list1[1] 不受影响，且两者内存地址不同。
可变类型元素：修改浅拷贝后容器中的可变类型元素（如 list、dict、set），会影响原容器中的对应元素。因为可变类型元素在浅拷贝后仍然引用相同的内存地址。例如修改 list3[2][0] 后，list1[2][0] 也会改变，且两者内存地址相同。
结论
浅拷贝仅复制容器本身，不递归复制容器内的所有元素，容器内的可变类型元素仍然共享内存地址。这意味着对可变类型元素的修改会在所有引用该元素的容器中体现。
"""

#深拷贝
import copy
#为list4容器开辟一个内存单元，那么测试一下list4是否共享list1对元素的引用？
# list4:  0x1d0b32bcd40
# list1:  0x1d0b316a2c0
list4 = copy.deepcopy(list1)
print(list4)
print('list4: ', hex(id(list4)))
print('list1: ', hex(id(list1)))
# list4[1]:  0x7ff8b3c021c8
# list1[1]:  0x7ff8b3c021c8
#为什么会相同：Python 为了优化内存使用，会重用相同的值。当多个变量引用相同的不可变对象时，它们会指向同一个内存地址。
print('list4[1]: ', hex(id(list4[1])))
print('list1[1]: ', hex(id(list1[1])))
# list4[2]:  0x1bb889dcac0
# list1[2]:  0x1bb888660c0
#对于可变数据类型，会开辟不同的内存单元，指向不同引用
print('list4[2]: ', hex(id(list4[2])))  # 输出 list4 中嵌套列表的内存地址
print('list1[2]: ', hex(id(list1[2])))  # 输出 list1 中嵌套列表的内存地址
